/*
 * jngc, Java utility classes by Nicholas Coltharp
 * Copyright (C) 2010 Nicholas Coltharp <ngc1@rice.edu>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package edu.rice.ngc1.util.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

/**
 * <p>
 * Annotation used to mark and verify classes whose instances are all
 * immutable ("immutable classes").
 * </p>
 * 
 * <p>
 * In general terms, an immutable object is an object whose state
 * cannot be changed after construction.
 * </p>
 * 
 * <p>
 * Formally, a class is immutable if and only if:
 * <ul>
 * <li>it is marked <code>final</code>;</li>
 * 
 * <li>none of its methods change its own public state; and</li>
 * 
 * <li>any mutable fields it has are not accessible by any means to
 * external classes.</li>
 * 
 * </ul>
 * </p>
 * 
 * This annotation is used to indicate that a class meets these
 * requirements, and to verify them based on varying degrees of
 * strictness. See {@link Strictness} for more details.
 * 
 * @see edu.rice.ngc1.util.annotation.processor.ImmutableProcessor
 */
@Target({ElementType.TYPE})
public @interface Immutable {

    /**
     * <p>
     * Indicates the degree to which a class marked with the
     * {@link Immutable} annotation should be checked by an
     * {@link edu.rice.ngc1.util.annotation.processor.ImmutableProcessor}
     * for compliance with the immutability contract. Each member of
     * this enum indicates a different set of requirements which, if
     * not met, will cause the annotation processor to fail.
     * </p>
     * 
     * <p>
     * For the purposes of verification, a variable is considered
     * immutable if it is either a primitive value or an instance of a
     * class marked with the Immutable annotation.
     * </p>
     * 
     * <p>
     * A field may be marked as mutable with the {@link Mutable}
     * annotation. This can be used for internal data that is not part
     * of the class's public state, or for internal members belonging
     * to types that it would be cumbersome to make formally
     * immutable.
     * </p>
     */
    public enum Strictness {

        /**
         * Makes no guarantees (but checks to ensure it is not on an
         * interface). You should prefer {@link #LOW} in almost all
         * cases.
         */
        NONE("NONE", false, true, false, true, true, true),

        /**
         * Guarantees that the class is {@code final}, that it has no
         * public members marked mutable, and that all of its fields
         * fields not marked as mutable are {@code final}.
         */
        LOW("NONE", true, false, true, true, true, true),

        /**
         * Additionally guarantees that all of the class's public
         * members are immutable.
         */
        MID("NONE", true, false, true, false, true, true),

        /**
         * Additionally guarantees that all of the class's private,
         * non-mutable members are immutable.
         */
        MID_HIGH("NONE", true, false, true, false, false, true),

        /**
         * Modifies the requirements of {@link #MID_HIGH} so that all
         * of the class's immutable fields must themselves be marked
         * with either {@code HIGH} or higher.
         */
        HIGH("HIGH", true, false, true, false, false, true),

        /**
         * Modifies the requirements of {@link #HIGH} so that the the
         * class may not have any members marked mutable and all of
         * its reference-type fields must be marked {@code MAX}. This
         * is the highest level of strictness, and classes marked with
         * this are guaranteed to be completely immutable.
         */
        MAX("MAX", true, false, true, false, false, false);

        private final String lowestRequiredStrictness;
        private final boolean finalClassRequired;
        private final boolean publicMutableFieldsAllowed;
        private final boolean finalNonmutableFieldsRequired;
        private final boolean publicNonImmutableFieldsAllowed;
        private final boolean privateNonImmutableFieldsAllowed;
        private final boolean mutableFieldsAllowed;

        Strictness(String requiredStrictness, boolean finalClass,
                boolean pubMutable, boolean finalNonmutable,
                boolean pubNonImmutable, boolean privNonImmutable,
                boolean mutableAllowed) {
            lowestRequiredStrictness = requiredStrictness;
            finalClassRequired = finalClass;
            publicMutableFieldsAllowed = pubMutable;
            finalNonmutableFieldsRequired = finalNonmutable;
            publicNonImmutableFieldsAllowed = pubNonImmutable;
            privateNonImmutableFieldsAllowed = privNonImmutable;
            mutableFieldsAllowed = mutableAllowed;
        }

        /**
         * Gets the lowest strictness that a given level requires of
         * its class's immutable fields.
         * 
         * @return the lowest required strictness
         */
        public Strictness getLowestRequiredStrictness() {
            return valueOf(Strictness.class, lowestRequiredStrictness);
        }

        /**
         * Indicates whether a given strictness level requires that
         * its class be {@code final}.
         * 
         * @return whether the class must be final
         */
        public boolean mustClassBeFinal() {
            return finalClassRequired;
        }

        /**
         * Indicates whether a given strictness level allows public
         * fields to be marked mutable.
         * 
         * @return whether public fields may be mutable
         */
        public boolean arePublicMutableFieldsAllowed() {
            return publicMutableFieldsAllowed;
        }

        /**
         * Indicates whether a given strictness level requires that
         * fields not marked mutable be marked final.
         * 
         * @return whether non-mutable fields must be final
         */
        public boolean mustNonmutableFieldsBeFinal() {
            return finalNonmutableFieldsRequired;
        }

        /**
         * Indicates whether a given strictness level allows public
         * fields to not be marked immutable.
         * 
         * @return whether public non-immutable fields are allowed
         */
        public boolean arePublicNonImmutableFieldsAllowed() {
            return publicNonImmutableFieldsAllowed;
        }

        /**
         * Indicates whether a given strictness level allows private
         * fields to not be marked immutable.
         * 
         * @return whether private non-immutable fields are allowed
         */
        public boolean arePrivateNonImmutableFieldsAllowed() {
            return privateNonImmutableFieldsAllowed;
        }

        /**
         * Indicates whether a given strictness level allows any
         * fields to be marked mutable.
         * 
         * @return whether mutable fields are allowed
         */
        public boolean areMutableFieldsAllowed() {
            return mutableFieldsAllowed;
        }
    }

    /**
     * The level of strictness required for the object annotated with
     * this annotation. Defaults to {@link Strictness#LOW}.
     * 
     * @return this annotation's strictness level
     */
    Strictness value() default Strictness.LOW;
}
